﻿using System;
using System.Collections.Generic;
using Engine;
using Game;

namespace Mekiasm
{
    public class MutiBlockCheck
    {
        public class AcceptBlock
        {
            public enum Type
            {
                Index, Data, Value
            }
            public int value;
            public Func<int, int, bool> CheckFunc;
            public AcceptBlock(int v, Func<int, int, bool> func = null)
            {
                value = v;
                if (func == null)
                {
                    CheckFunc = (int value1, int value2) => { return value1 == value2; };
                }
                else CheckFunc = func;
            }
        }
        public List<Point3> cache = new List<Point3>();
        public class TranmitResult
        {
            public Point3 bottom;
            public Point3 top;
            public Point3 centerPoint;
            public List<Point3> insidePoints;
            public Point3 Connection;
            public bool finished;
            public bool directionIsX;
            public bool Connected;
        }
        public struct Result
        {
            public List<Point3> blocks;
            public Point3 Size;
            public bool finish;
            public string err;
            public Point3 savaPoint;
        }

        public Result createResult()
        {
            Result result = new Result();
            result.finish = false;
            return result;
        }
        public static Point3 caculateV(List<Point3> point3s)
        {//计算长宽高
            if (point3s.Count < 25) return Point3.Zero;
            return new Point3(point3s[0].X - point3s[point3s.Count - 1].X + 1, point3s[0].Y - point3s[point3s.Count - 1].Y + 1, point3s[0].Z - point3s[point3s.Count - 1].Z + 1);
        }
        public static bool checkExists(List<Point3> point3s, List<int> outsideBlocks, List<int> insideBlocks, List<int> sideBlocks, List<int> faceBlocks, Terrain terrain)
        {
            Point3 point; int vv;
            if (point3s.Count < 25) return false;
            int j = point3s.Count - 1;
            for (int y = 0; y <= point3s.Count; y++)
            {
                for (int i = point3s[j].X; i <= point3s[0].X; i++)
                {
                    for (int m = point3s[j].Y; m <= point3s[0].Y; m++)
                    {
                        for (int n = point3s[j].Z; n <= point3s[0].Z; n++)
                        {
                            point = new Point3(i, m, n);
                            if (!point3s.Contains(point) && !insideBlocks.Contains(0)) return false;
                            vv = terrain.GetCellValue(i, m, n);
                            vv = Terrain.MakeBlockValue(Terrain.ExtractContents(vv), 0, Terrain.ExtractData(vv));
                            if (i == point3s[j].X || i == point3s[0].X || m == point3s[j].Y || m == point3s[0].Y || n == point3s[j].Z || n == point3s[0].Z)
                            {//外部点

                                if ((i == point3s[j].X || i == point3s[0].X) && (m > point3s[j].Y && m < point3s[0].Y) && (n > point3s[j].Z && n < point3s[0].Z))
                                {//面心点
                                    if (faceBlocks != null) if (!faceBlocks.Contains(vv)) { Log.Information($"{Terrain.ExtractData(vv)}不是面心所需:{point3s.Count}:{i}:{m}:{n}"); return false; }
                                }
                                else if ((i > point3s[j].X && i < point3s[0].X) && (m == point3s[j].Y || m == point3s[0].Y) && (n > point3s[j].Z && n < point3s[0].Z))
                                {//面心点
                                    if (faceBlocks != null) if (!faceBlocks.Contains(vv)) { Log.Information($"{Terrain.ExtractData(vv)}不是面心所需:{point3s.Count}:{i}:{m}:{n}"); return false; }
                                }
                                else if ((i > point3s[j].X && i < point3s[0].X) && (m > point3s[j].Y && m < point3s[0].Y) && (n == point3s[j].Z || n == point3s[0].Z))
                                {//面心点
                                    if (faceBlocks != null) if (!faceBlocks.Contains(vv)) { Log.Information($"{Terrain.ExtractData(vv)}不是面心所需:{point3s.Count}:{i}:{m}:{n}"); return false; }
                                }
                                else
                                {
                                    //框架上的点
                                    if (!sideBlocks.Contains(vv))
                                    {
                                        Log.Information($"{Terrain.ExtractData(vv)}不是框架所需"); return false;
                                    }
                                }
                                if (sideBlocks == null && faceBlocks == null) if (!outsideBlocks.Contains(vv))
                                    {
                                        Log.Information($"{Terrain.ExtractData(vv)}不是外部所需"); return false;
                                    }

                            }
                            else
                            { //内部点
                                if (!insideBlocks.Contains(vv)) { Log.Information($"{Terrain.ExtractData(vv)}不是内部所需"); return false; }
                            }
                        }
                    }

                }
            }
            return true;
        }
        public List<Point3> findSameBlocks(Terrain terrain, Point3 start, List<AcceptBlock> acceptBlocks)
        {
            cache.Clear();
            cache.Add(start);
            findSameBlocksLogic(terrain, start, acceptBlocks);
            return cache;
        }
        public void findSameBlocksLogic(Terrain terrain, Point3 start, List<AcceptBlock> acceptBlocks)
        {
            for (int i = 0; i < 6; i++)
            {
                Point3 point = start + CellFace.FaceToPoint3(i);
                int value = terrain.GetCellValueFast(point.X, point.Y, point.Z);
                bool flag = false;
                foreach (AcceptBlock acceptBlock in acceptBlocks)
                {
                    if (acceptBlock.CheckFunc(acceptBlock.value, value))
                    {
                        flag = true; break;
                    }
                }
                if (!cache.Contains(point) && flag)
                {
                    cache.Add(point);
                    findSameBlocksLogic(terrain, point, acceptBlocks);
                }
            }
        }
        public static void sortPoint3(List<Point3> point3s)
        {//由大到小
            for (int i = 0; i < point3s.Count; i++)
            {
                for (int j = 1; j < point3s.Count - i; j++)
                {
                    if (point3s[j - 1].X < point3s[j].X)
                    {
                        Point3 point = point3s[j - 1];
                        point3s[j - 1] = point3s[j];
                        point3s[j] = point;
                    }
                    else if (point3s[j - 1].X == point3s[j].X)
                    {
                        if (point3s[j - 1].Y < point3s[j].Y)
                        {
                            Point3 point = point3s[j - 1];
                            point3s[j - 1] = point3s[j];
                            point3s[j] = point;
                        }
                        else if (point3s[j - 1].Y == point3s[j].Y)
                        {
                            if (point3s[j - 1].Z < point3s[j].Z)
                            {
                                Point3 point = point3s[j - 1];
                                point3s[j - 1] = point3s[j];
                                point3s[j] = point;
                            }
                        }
                    }
                }
            }
        }
        public TranmitResult checkTransmit(SubsystemTerrain subsystemTerrain, Point3 point, List<AcceptBlock> acceptBlocks, int centerBlock)
        {
            cache.Clear();
            TranmitResult tranmitResult = new TranmitResult();
            tranmitResult.insidePoints = new List<Point3>();
            Terrain terrain = subsystemTerrain.Terrain;
            findSameBlocks(terrain, point, acceptBlocks);
            sortPoint3(cache);
            tranmitResult.finished = false;
            if (cache.Count != 10) { return tranmitResult; }
            Point3 bottom = cache[cache.Count - 1];
            Point3 top = cache[0];
            int centerP = 0; int centerV = 0;
            if ((top.X - bottom.X) != 0)
            { //x方向
                centerP = (top.X + bottom.X) / 2;
                centerV = (ILibrary.SubsystemTerrain.Terrain.GetCellValueFast(centerP, bottom.Y, bottom.Z));
                tranmitResult.insidePoints.Add(new Point3(centerP, top.Y - 1, top.Z));
                tranmitResult.insidePoints.Add(new Point3(centerP, top.Y - 2, top.Z));
                tranmitResult.directionIsX = true;
            }
            else if ((top.Z - bottom.Z) != 0)//z方向
            {
                centerP = (top.Z + bottom.Z) / 2;
                centerV = ILibrary.SubsystemTerrain.Terrain.GetCellValueFast(bottom.X, bottom.Y, centerP);
                tranmitResult.insidePoints.Add(new Point3(top.X, top.Y - 1, centerP));
                tranmitResult.insidePoints.Add(new Point3(top.X, top.Y - 2, centerP));
            }
            if (!(Terrain.ExtractContents(centerV) == Terrain.ExtractContents(TransmitMachine.Index)) || !(Terrain.ExtractData(centerV) == Terrain.ExtractData(TransmitMachine.Index)))
            {
                return tranmitResult;
            }
            tranmitResult.finished = true;
            tranmitResult.bottom = bottom;
            tranmitResult.top = top;
            return tranmitResult;
        }
    }
}
